# -*- coding: utf-8 -*-
# keywords_heat.py
# Created by Hardy on 24th, Feb
# Copyright 2017 杭州网川教育科技有限公司. All rights reserved.

from querier.esquerier import ElasticSearchQuerier
from querier.weibo import utils
import datetime


class WeiboPubStatMultiQuerier(ElasticSearchQuerier):
    def __init__(self, search_heat_querier):
        super(WeiboPubStatMultiQuerier, self).__init__(None, None, None)
        self.search_heat_querier = search_heat_querier

    def search(self, args):
        date = datetime.datetime.now()
        default_end_time = date.strftime('%Y-%m-%d %H:%M:%S')

        keywords = args.get('keywords')
        from_date = args.get('from_date')
        to_date = args.get('to_date')
        if from_date is None:
            from_date = '2017-12-01 00:00:00'
        if to_date is None:
            to_date = default_end_time
        res = []
        for w in keywords:
            a = {"word": w, "from_date": from_date, "to_date": to_date}
            res.append(self.search_heat_querier.search(a))
        return {'pub_stat': res}

    def _build_query(self, args): pass

    def _build_result(self, es_result, param): pass


class WeiboPubStatQuerier(ElasticSearchQuerier):
    def __init__(self, es, index, doc_type):
        super(WeiboPubStatQuerier, self).__init__(es, index, doc_type)

    def _build_query(self, args):
        date = datetime.datetime.now()
        default_end_time = date.strftime('%Y-%m-%d %H:%M:%S')
        word = args.get('word')
        from_date = args.get('from_date')
        to_date = args.get('to_date')
        if from_date is None:
            from_date = '2017-12-01 00:00:00'
        if to_date is None:
            to_date = default_end_time
        filters = dict()
        if word:
            filters['keywords'] = word.split(';')

        from_date_t = utils.try_parsing_date(from_date)
        to_date_t = utils.try_parsing_date(to_date) + datetime.timedelta(days=1)

        to_date = to_date_t.strftime('%Y%m%d')

        filters['publish_timestamp'] = [from_date, to_date]

        d = to_date_t - from_date_t

        dates = [(from_date_t + datetime.timedelta(x)).strftime('%Y-%m-%d') for x in range(d.days)]

        query = self._gen_query(filters)
        return query, {}, {'keywords': filters['keywords'], 'from_date': from_date, 'to_date': to_date, 'dates': dates}

    @staticmethod
    def _build_result(es_result, param):
        # total = es_result['hits']['total']
        agg = es_result['aggregations']
        data = extract_result(agg)
        word_df = dict(zip(data['dates'], data['doc_counts']))
        sentiment_negative_df = dict(zip(data['dates'], data['sentiment_negative']))
        sentiment_neutral_df = dict(zip(data['dates'], data['sentiment_neutral']))
        sentiment_positive_df = dict(zip(data['dates'], data['sentiment_positive']))
        sentiment_negative = [sentiment_negative_df.get(d, 0) for d in param['dates']]
        sentiment_neutral = [sentiment_neutral_df.get(d, 0) for d in param['dates']]
        sentiment_positive = [sentiment_positive_df.get(d, 0) for d in param['dates']]
        df = [word_df.get(d, 0) for d in param['dates']]
        total = sum(df)
        sentiment_negative_total = sum(sentiment_negative)
        sentiment_neutral_total = sum(sentiment_neutral)
        sentiment_positive_total = sum(sentiment_positive)
        return {
            'total': total,
            'sentiment_negative_total':sentiment_negative_total,
            'sentiment_neutral_total': sentiment_neutral_total,
            'sentiment_positive_total': sentiment_positive_total,
            'df': df,
            'dates': param['dates'],
            'sentiment_negative': sentiment_negative,
            'sentiment_neutral': sentiment_neutral,
            'sentiment_positive': sentiment_positive,
            'word': param['keywords'][0]
        }

    def _gen_query(self, filters):
        filter_clause = []

        if filters:
            filter_clause = self._add_filter_range_clause(filter_clause, filters, 'publish_timestamp')
            filter_clause = self._add_filter_clause(filter_clause, filters, 'source')
            for k in filters.get('keywords', []):
                filter_clause = self._add_filter_clause_search(filter_clause, [k])

        query = {
            "query": {
                "bool": {
                    "filter": filter_clause
                }
            },
            "aggs": {
                "time_hist": {
                    "date_histogram": {
                        "field": "publish_timestamp",
                        "interval": "1d"
                        # "interval": "1w"
                    },
                    "aggs": {
                        "sum_likes": {"sum": {"field": "likes", "missing": 0}},
                        "sum_negative": {
                            "sum": {
                                "script": {
                                    'lang': 'painless',
                                    'inline': "if (doc.full_sentiment_category.value == 1 "
                                              "|| doc.full_sentiment_category.value == 2) {return 1} else {return 0}"
                                }
                            }
                        },
                        "sum_neutral": {
                            "sum": {
                                "script": {
                                    'lang': 'painless',
                                    'inline': "if (doc.full_sentiment_category.value == 3) {return 1} else {return 0}"
                                }
                            }
                        },
                        "sum_positive": {
                            "sum": {
                                "script": {
                                    'lang': 'painless',
                                    'inline': "if (doc.full_sentiment_category.value == 4 "
                                              "|| doc.full_sentiment_category.value == 5) {return 1} else {return 0}"
                                }
                            }
                        }
                    }
                }
            },
            "size": 0
        }

        return query

    @staticmethod
    def _add_filter_match(must_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                must_clause.append({
                    'bool': {'should': clause}
                })
                values = filters[key]
                if isinstance(values, str):
                    values = values.split(' ')
                for fk in values:
                    clause.append({'match': {key: {'query': fk, 'minimum_should_match': '10<85% 20<10'}}})
        return must_clause

    @staticmethod
    def _add_filter_clause_search(filter_clause, keywords):
        clause = []
        filter_clause.append({
            'bool': {
                'should': clause,
                'minimum_should_match': 1
            }
        })
        for fk in keywords:
            clause.append({'term': {'keywords': fk}})
            clause.append({'match_phrase': {"weibo_text": {"query": fk}}})
        return filter_clause

    @staticmethod
    def _add_filter_clause(filter_clause, filters, key, cond='must'):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        cond: clause
                    }
                })
                for fk in filters[key]:
                    clause.append({'term': {key: fk}})
        return filter_clause

    @staticmethod
    def _add_filter_range_clause(filter_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        'must': clause
                    }
                })
                fk = filters[key]
                if not isinstance(fk, list) or len(fk) < 1:
                    pass
                else:
                    min_fk = fk[0]
                    if len(fk) >= 2:
                        max_fk = fk[1]
                    else:
                        max_fk = None
                    if min_fk is not None and min_fk != 'null':
                        clause.append({'range': {key: {"gte": min_fk}}})
                    if max_fk is not None and max_fk != 'null':
                        clause.append({'range': {key: {"lte": max_fk}}})
        return filter_clause


def extract_result(agg):
    buckets = agg['time_hist']['buckets']
    doc_counts = []
    sum_likes = []
    dates = []
    sum_negative = []
    sum_neutral = []
    sum_positive = []
    for b in buckets:
        doc_counts.append(b['doc_count'])
        sum_likes.append(b['sum_likes']['value'])
        sum_negative.append(b['sum_negative']['value'])
        sum_neutral.append(b['sum_neutral']['value'])
        sum_positive.append(b['sum_positive']['value'])
        dates.append(b['key_as_string'])

    return {
        'doc_counts': doc_counts,
        'sum_likes': sum_likes,
        'sentiment_negative': sum_negative,
        'sentiment_neutral': sum_neutral,
        'sentiment_positive': sum_positive,
        'dates': dates
    }
